package com.lenovo.lcdm.type.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.lenovo.lcdm.common.enums.CommonMsgEnum;
import com.lenovo.lcdm.common.enums.DeleteFlagEnum;
import com.lenovo.lcdm.common.exception.ServiceException;
import com.lenovo.lcdm.type.util.ContextUtil;
import com.lenovo.lcdm.type.dto.LocalAttributeDTO;
import com.lenovo.lcdm.type.dto.TypeDefinitionDTO;
import com.lenovo.lcdm.type.entity.*;
import com.lenovo.lcdm.type.mapper.TypeLocalAttributeMapper;
import com.lenovo.lcdm.type.service.*;
import com.lenovo.lcdm.type.util.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
* @author xujing30
* @description 针对表【type_local_attribute】的数据库操作Service实现
* @createDate 2024-01-03 16:33:07
*/
@Service
public class TypeLocalAttributeServiceImpl extends ServiceImpl<TypeLocalAttributeMapper, TypeLocalAttribute>
    implements TypeLocalAttributeService{

    private final Logger logger = LoggerFactory.getLogger(TypeLocalAttributeServiceImpl.class);

    @Autowired
    private TypeLocalAttributeLinkService typeLocalAttributeLinkService;

    @Autowired
    @Lazy
    private TypeGlobalAttributeService globalAttributeService;

    @Autowired
    @Lazy
    private TypeDefinitionService typeDefinitionService;

    @Autowired
    private TypeAttributeDataTypeSeqService attributeDataTypeSeqService;

    @Autowired
    private TypeDataTypeService dataTypeService;

    @Autowired
    private TypeAttributeGroupAttributeService attributeGroupAttributeService;

    @Override
    public void saveTypeLocalAttribute(LocalAttributeDTO localAttributeDTO) {
        // 属性检验
        LocalAttributeUtil.verifyAddLocalAttribute(typeDefinitionService.queryDetail(localAttributeDTO.getTypeId()),
                localAttributeDTO.getGlobalAttributeId(),
                CommonUtils.safeConvert(
                        globalAttributeService.findGlobalAttributeDataType(localAttributeDTO.getGlobalAttributeId()),
                        TypeDataType::getName),
                localAttributeDTO.getGroupId(),
                localAttributeDTO.getReferencedAttribute(),
                localAttributeDTO.getReferencedValueProvider());
        //保存local属性基本信息
        TypeLocalAttribute localAttribute = new TypeLocalAttribute();
        localAttribute.setGlobalAttributeId(localAttributeDTO.getGlobalAttributeId());
        localAttribute.setMultipleValue(localAttributeDTO.isMultipleValue());
        localAttribute.setValidInputList(localAttributeDTO.getValidInputList());
        localAttribute.setValueInputStyle(localAttributeDTO.getValueInputStyle());
        localAttribute.setValueSelectionStyle(localAttributeDTO.getValueSelectionStyle());
        localAttribute.setDescription(localAttributeDTO.getDescription());
        localAttribute.setCreator(ContextUtil.getCurrentUser());
        localAttribute.setCreateTime(LocalDateTime.now());
        localAttribute.setModifier(ContextUtil.getCurrentUser());
        localAttribute.setModifyTime(LocalDateTime.now());
        localAttribute.setObjectClass(localAttribute.getClass().getName());
        // 校验referencedAttribute

        localAttribute.setReferencedAttribute(localAttributeDTO.getReferencedAttribute());
        localAttribute.setReferencedValueProvider(localAttributeDTO.getReferencedValueProvider());
        localAttribute.setFormat(localAttributeDTO.getFormat());

        String internalId = this.assignInternalId(localAttributeDTO.getGlobalAttributeId(),
                localAttributeDTO.getTypeId(),
                localAttributeDTO.getDataTypeName(),
                this.getInternalIdMap(localAttributeDTO.getTypeId()));
        Assert.notNull(internalId, "assign internal id failed");

        localAttribute.setInternalId(internalId);
        this.save(localAttribute);

        //保存type local属性link信息

        TypeLocalAttributeLink typeLocalAttributeLink = new TypeLocalAttributeLink();
        typeLocalAttributeLink.setTypeId(localAttributeDTO.getTypeId());
        typeLocalAttributeLink.setLocalAttributeId(localAttribute.getId());
        typeLocalAttributeLink.setCreator(ContextUtil.getCurrentUser());
        typeLocalAttributeLink.setModifier(ContextUtil.getCurrentUser());
        typeLocalAttributeLink.setObjectClass(typeLocalAttributeLink.getClass().getName());
        typeLocalAttributeLinkService.save(typeLocalAttributeLink);

        // 保存分组信息
        TypeAttributeGroupAttribute attributeGroupAttribute = new TypeAttributeGroupAttribute();
        attributeGroupAttribute.setLocalAttributeId(localAttribute.getId());
        attributeGroupAttribute.setGroupId(localAttributeDTO.getGroupId());
        attributeGroupAttribute.setTypeId(localAttributeDTO.getTypeId());
        attributeGroupAttribute.setCreator(ContextUtil.getCurrentUser());
        attributeGroupAttribute.setCreateTime(LocalDateTime.now());
        attributeGroupAttribute.setModifier(ContextUtil.getCurrentUser());
        attributeGroupAttribute.setModifyTime(LocalDateTime.now());
        attributeGroupAttributeService.save(attributeGroupAttribute);
    }

    public String assignInternalId(long globalAttributeId, long typeId, String dataTypeName, Map<Long, Map<Long, String>> typeInternalIdMap) {
        // 当前全局属性是否已经有了internal id
        TypeDefinition rootTypeDefinition = typeDefinitionService.getRootTypeDefinition(typeId);
        if (Objects.isNull(rootTypeDefinition)) {
            logger.info("assignInternalId rootTypeDefinition not exist,typeId:{} ", typeId);
            return null;
        }

        Long rootTypeId = rootTypeDefinition.getId();
        String existInternalId = typeInternalIdMap.getOrDefault(rootTypeId, Collections.emptyMap()).get(globalAttributeId);
        if (StringUtils.isNotEmpty(existInternalId)) {
            return existInternalId;
        }

        TypeGlobalAttribute globalAttribute = globalAttributeService.getById(globalAttributeId);
        int index = assignInternalId(globalAttribute, typeId);
        String internalId = index <0 ? null : dataTypeName.toLowerCase() + index;
        // 更新缓存数据
        typeInternalIdMap.computeIfAbsent(rootTypeId, k -> Maps.newHashMap()).put(globalAttributeId, internalId);
        return internalId;
    }

    /**
     *
     * 只不过是只获取指定类型跟类型下的所有internal id信息
     * 现有数据已经存在重复的，可以忽略取第一个
     */
    @Override
    public Map<Long, Map<Long, String>> getInternalIdMap(Long typeId) {
        Map<Long, Map<Long, String>> result = new HashMap<>();
        TypeDefinition rootTypeDefinition = typeDefinitionService.getRootTypeDefinition(typeId);
        if(rootTypeDefinition == null){
            return result;
        }

        // 获取root type下的所有子类型
        TypeDefinitionDTO rootTypeDefinitionDTO =
                typeDefinitionService.findTypeDefinitionAttributeRecursion(rootTypeDefinition);

        TypeDefinitionDTOWalker.preOrderWalk(
                Lists.newArrayList(rootTypeDefinitionDTO),
                typeDefinitionDTO -> {
                    List<LocalAttributeDTO> localAttributes = typeDefinitionDTO.getLocalAttributes();
                    if (!CollectionUtils.isEmpty(localAttributes)) {
                        localAttributes.forEach(localAttribute -> {
                            if (ObjectUtil.isAnyEmpty(localAttribute.getInternalId())) {
                                return;
                            }

                            // 现有数据已经存在重复的，可以忽略取最后一个
                            result.computeIfAbsent(rootTypeDefinition.getId(), k -> Maps.newHashMap())
                                    .put(localAttribute.getGlobalAttributeId(), localAttribute.getInternalId());
                        });
                    }
                });
        return result;
    }

    @Override
    public List<TypeLocalAttribute> findAll() {
        LambdaQueryWrapper<TypeLocalAttribute> qw = new LambdaQueryWrapper();
        qw.eq(TypeLocalAttribute::getDeleted, DeleteFlagEnum.NO.getValue());
        return list(qw);
    }


    public int assignInternalId(TypeGlobalAttribute globalAttribute, long typeId){
        Long dataTypeId = globalAttribute.getDataTypeId();
        TypeDefinition typeDefinition = typeDefinitionService.getRootTypeDefinition(typeId);
        if(typeDefinition == null){
            return -1;
        }
        TypeAttributeDataTypeSeq attributeDataTypeSeq = attributeDataTypeSeqService.getAttributeDataTypeSeq(typeDefinition.getId(), dataTypeId);
        //if seq is not assigned, then new seq,
        if( attributeDataTypeSeq == null){
            attributeDataTypeSeq = new TypeAttributeDataTypeSeq();
            attributeDataTypeSeq.setDataTypeId(dataTypeId);
            attributeDataTypeSeq.setTypeDefinitionId(typeDefinition.getId());
            attributeDataTypeSeq.setSeq(1);
            attributeDataTypeSeq.setCreateTime(LocalDateTime.now());
            attributeDataTypeSeq.setModifyTime(LocalDateTime.now());
            attributeDataTypeSeq.setCreator(ContextUtil.getCurrentUser());

        } else {
            attributeDataTypeSeq.setSeq(attributeDataTypeSeq.getSeq() + 1);
            attributeDataTypeSeq.setModifyTime(LocalDateTime.now());
        }
        attributeDataTypeSeq.setModifier(ContextUtil.getCurrentUser());
        attributeDataTypeSeqService.saveOrUpdate(attributeDataTypeSeq);
        return attributeDataTypeSeq.getSeq();
    }

    @Override
    public void updateTypeLocalAttribute(LocalAttributeDTO localAttributeDTO){
        //更新local属性基本信息
        TypeLocalAttribute localAttribute = getById(localAttributeDTO.getId());
        if (localAttribute != null) {
            localAttribute.setGlobalAttributeId(localAttributeDTO.getGlobalAttributeId());
            localAttribute.setMultipleValue(localAttributeDTO.isMultipleValue());
            localAttribute.setValidInputList(localAttributeDTO.getValidInputList());
            localAttribute.setValueInputStyle(localAttributeDTO.getValueInputStyle());
            localAttribute.setValueSelectionStyle(localAttributeDTO.getValueSelectionStyle());
            localAttribute.setDescription(localAttributeDTO.getDescription());
            localAttribute.setModifier(ContextUtil.getCurrentUser());
            // 校验referencedAttribute

            localAttribute.setReferencedAttribute(localAttributeDTO.getReferencedAttribute());
            localAttribute.setReferencedValueProvider(localAttributeDTO.getReferencedValueProvider());
            localAttribute.setFormat(localAttributeDTO.getFormat());


            // 校验属性
            LocalAttributeUtil.verifyUpdateRefLocalAttribute(typeDefinitionService.queryDetail(localAttributeDTO.getTypeId()),
                    localAttribute.getId(),
                    localAttribute.getGlobalAttributeId(),
                    CommonUtils.safeConvert(globalAttributeService.findGlobalAttributeDataType(localAttributeDTO.getGlobalAttributeId()), TypeDataType::getName),
                    localAttribute.getReferencedAttribute(),
                    localAttribute.getReferencedValueProvider());

            this.saveOrUpdate(localAttribute);
        } else {
            throw new ServiceException("Update local attribute fail,can not find local attribute" , CommonMsgEnum.FAIL.getCode());
        }
    }

    @Override
    public void deleteTypeLocalAttribute(Long localAttributeId) {
        TypeLocalAttribute localAttribute = getById(localAttributeId);
        if (null != localAttribute) {
            TypeLocalAttributeLink localAttributeLink = typeLocalAttributeLinkService.
                    queryByLocalAttributeId(localAttribute.getId());
            if (null != localAttributeLink) {
                typeLocalAttributeLinkService.removeById(localAttributeLink.getId());
                this.removeById(localAttributeId);
                // 删除排序
                typeDefinitionService.deleteAttributeSort(localAttributeLink.getId(), localAttributeLink.getTypeId());
                //删除组
                LambdaQueryWrapper<TypeAttributeGroupAttribute> attributeGroupAttributeQw = new LambdaQueryWrapper<>();
                attributeGroupAttributeQw.eq(TypeAttributeGroupAttribute::getLocalAttributeId, localAttributeId);
                attributeGroupAttributeService.remove(attributeGroupAttributeQw);
            } else {
                throw new ServiceException("TypeLocalAttributeLink Object is not exist" ,CommonMsgEnum.FAIL.getCode());
            }
        } else {
            throw new ServiceException("TypeLocalAttribute Object is not exist" ,CommonMsgEnum.FAIL.getCode());
        }
    }

    @Override
    public LocalAttributeDTO getLocalAttributeDetail(Long  localAttributeId) {
        LocalAttributeDTO localAttributeDTO = new LocalAttributeDTO();
        TypeLocalAttribute localAttribute = getById(localAttributeId);
        if (localAttribute != null) {
            localAttributeDTO.setId(localAttribute.getId());
            localAttributeDTO.setDescription(localAttribute.getDescription());
            localAttributeDTO.setReferencedAttribute(localAttribute.getReferencedAttribute());
            localAttributeDTO.setReferencedValueProvider(localAttribute.getReferencedValueProvider());
            localAttributeDTO.setFormat(localAttribute.getFormat());
            TypeGlobalAttribute typeGlobalAttribute = globalAttributeService.getById(localAttribute.getGlobalAttributeId());
            TypeLocalAttributeLink typeLocalAttributeLink = typeLocalAttributeLinkService.queryByLocalAttributeId(localAttributeId);
            if (typeLocalAttributeLink != null) {
                localAttributeDTO.setTypeId(typeLocalAttributeLink.getTypeId());
            }
            if (typeGlobalAttribute != null) {
                localAttributeDTO.setGlobalAttributeId(typeGlobalAttribute.getId());
                localAttributeDTO.setName(typeGlobalAttribute.getName());
                localAttributeDTO.setDisplayName(typeGlobalAttribute.getDisplayName());
                TypeDataType dmfDataType = dataTypeService.getById(typeGlobalAttribute.getDataTypeId());
                if (dmfDataType != null) {
                    localAttributeDTO.setDataTypeName(dmfDataType.getName());
                }
                localAttributeDTO.setMultipleValue(localAttribute.getMultipleValue());
                localAttributeDTO.setValidInputList(localAttribute.getValidInputList());
                localAttributeDTO.setValueInputStyle(localAttribute.getValueInputStyle());
                localAttributeDTO.setValueSelectionStyle(localAttribute.getValueSelectionStyle());                
                return localAttributeDTO;            
            } else {
                throw new ServiceException("TypeGlobalAttribute Object is not exist" ,CommonMsgEnum.FAIL.getCode());
            }
        } else {
            throw new ServiceException("TypeLocalAttribute Object is not exist" ,CommonMsgEnum.FAIL.getCode());
        }
    }

    @Override
    public void updateInternalId(boolean ignoreExistingValue) {
        List<TypeLocalAttribute> localAttributes = findAll();
        List<Long> localAttributeIds = localAttributes.stream().map(TypeLocalAttribute::getId).collect(Collectors.toList());
        List<TypeLocalAttributeLink> localAttributeLinks = typeLocalAttributeLinkService.findByLocalAttributeIdIn(localAttributeIds);
        Map<Long, Long> attributeTypeMap = localAttributeLinks.stream()
                .collect(Collectors.toMap(TypeLocalAttributeLink::getLocalAttributeId,
                        TypeLocalAttributeLink::getTypeId));

        Map<Long, TypeGlobalAttribute> globalAttributes = getGlobalAttributes(localAttributes);
        List<TypeDataType> dmfDataTypes = dataTypeService.findAll();
        Map<Long, String> dataTypeMap = dmfDataTypes.stream()
                .collect(Collectors.toMap(TypeDataType::getId, TypeDataType::getName));
        // 跟类型下，包含所有子类型和跟类型的属性，的全局属性id和internal id对应关系
        Map<Long, Map<Long, String>> typeInternalIdMap = getAllTypeInternalIdMap();
        localAttributes = localAttributes.stream()
                .filter(localAttribute -> attributeTypeMap.containsKey(localAttribute.getId()))
                .filter(localAttribute -> ignoreExistingValue || localAttribute.getInternalId() == null)
                .map(localAttribute -> {
                    Long globalAttributeId = localAttribute.getGlobalAttributeId();
                    TypeGlobalAttribute TypeGlobalAttribute = globalAttributes.get(globalAttributeId);
                    String dataTypeName = dataTypeMap.get(TypeGlobalAttribute.getDataTypeId());

                    String internalId = assignInternalId(localAttribute.getGlobalAttributeId(),
                            attributeTypeMap.get(localAttribute.getId()),
                            dataTypeName,
                            typeInternalIdMap);
                    if (Objects.isNull(internalId)) {
                        logger.info("refreshInternalId assign internalId failed, localAttribute:{}", localAttribute.getId());
                        return null;
                    }

                    localAttribute.setInternalId(internalId);
                    localAttribute.setModifier(ContextUtil.getCurrentUser());
                    localAttribute.setModifyTime(LocalDateTime.now());
                    return localAttribute;
                }).filter(Objects::nonNull).collect(Collectors.toList());
        saveOrUpdateBatch(localAttributes);
    }

    /**
     * 获取指定类型根类型下所有全局属性，和其已经分配的internal id
     *
     *  现有数据已经存在重复的，可以忽略取第一个
     * @return root type id,  global attribute id,  internal id
     */    
    public Map<Long, Map<Long, String>> getAllTypeInternalIdMap() {
        // 找到local attribute和root type的对应关系
        // 查询全部信息
        List<TypeLocalAttribute> localAttributes = findAll();
        if (CollectionUtils.isEmpty(localAttributes)) {
            return Maps.newHashMap();
        }

        List<TypeDefinition> allTypes = typeDefinitionService.findAll();
        Map<Long, TypeDefinition> allTypeMap =
                StreamUtils.safeGroup(allTypes, TypeDefinition::getId, Function.identity());
        // 查找类型对应的根类型
        Map<Long, Long> typeToRootType = new HashMap<>(allTypes.size());
        allTypeMap.values().forEach(type -> {
            if (Objects.equals(type.getId(), 0L)) {
                return;
            }

            Long rootType = getRootType(allTypeMap, type.getId());
            if (Objects.isNull(rootType)) {
                return;
            }
            typeToRootType.put(type.getId(), rootType);
        });

        Map<Long, Map<Long, String>> result = new HashMap<>();
        // 获取属性类型对应关系
        Map<Long, Long> localAttrIdToTypeId = StreamUtils.safeGroup(typeLocalAttributeLinkService.list(),
                TypeLocalAttributeLink::getLocalAttributeId,
                TypeLocalAttributeLink::getTypeId);
        localAttributes.forEach(localAttribute -> {
            if (ObjectUtil.isAnyEmpty(localAttribute.getInternalId())) {
                return;
            }

            Long typeIdByLocalAttributeId = localAttrIdToTypeId.get(localAttribute.getId());
            if (Objects.isNull(typeIdByLocalAttributeId)) {
                return;
            }

            Long rootTypeId = typeToRootType.get(typeIdByLocalAttributeId);
            if (Objects.isNull(rootTypeId)) {
                return;
            }

            result.computeIfAbsent(rootTypeId, k -> Maps.newHashMap())
                    .put(localAttribute.getGlobalAttributeId(), localAttribute.getInternalId());
        });
        return result;
    }

    /**
     * 返回类型的根类型id
     */
    private Long getRootType(Map<Long, TypeDefinition> allTypes, Long typeId) {
        TypeDefinition typeDefinition = allTypes.get(typeId);
        if (Objects.isNull(typeDefinition)) {
            logger.info("getRootType, typeDefinition not exist, typeId:{}", typeId);
            return null;
        }

        if (Objects.equals(typeDefinition.getParentTypeId(), 0L)) {
            return typeDefinition.getId();
        }
        return getRootType(allTypes, typeDefinition.getParentTypeId());
    }

    
    public Map<Long, TypeGlobalAttribute> getGlobalAttributes(List<TypeLocalAttribute> localAttributes) {
        List<Long> globalIdList = localAttributes.stream().map(TypeLocalAttribute::getGlobalAttributeId).distinct().collect(Collectors.toList());
        List<TypeGlobalAttribute> globalList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(globalIdList)) {
            globalList = globalAttributeService.listByIds(globalIdList);
        }
        return globalList.stream().collect(Collectors.toMap(TypeGlobalAttribute::getId, Function.identity()));
    }

    @Override
    public List<TypeLocalAttribute> queryByGlobalAttributeId(Long id) {
        LambdaQueryWrapper<TypeLocalAttribute> qw = new LambdaQueryWrapper<>();
        qw.eq(TypeLocalAttribute::getGlobalAttributeId, id).eq(TypeLocalAttribute::getDeleted, DeleteFlagEnum.NO.getValue());
        return list(qw);
    }

    @Override
    public String queryByGlobalAttributeIdAndType(Long typeId, Long globalAttributeId){
        Map<Long, String> localAttributeMap = this.getGlobalAttributeInternalIdMap(typeId);
        return localAttributeMap.get(globalAttributeId);
    }

    @Override
    public Map<Long, String> getGlobalAttributeInternalIdMap(Long typeId){
        List<TypeLocalAttributeLink> linkList = typeLocalAttributeLinkService.queryByTypeId(typeId);
        if(CollectionUtils.isEmpty(linkList)){
            return null;
        }
        List<Long> localAttributeIds = linkList.stream().map(TypeLocalAttributeLink::getLocalAttributeId).toList();
        List<TypeLocalAttribute> localAttributes = listByIds(localAttributeIds);
        if(CollectionUtils.isEmpty(localAttributes)){
            return null;
        }
        return localAttributes.stream().collect(Collectors.toMap(TypeLocalAttribute::getGlobalAttributeId, TypeLocalAttribute::getInternalId));
    }

}




